/**
 *   Copyright 2008 Alexey Vishentsev
 *
 *   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * 
 */


package com.gwtspreadsheetinput.gwt.client;


import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.ScrollListener;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;

public class DynamicGrid extends Composite implements ScrollListener{
	private String elementId;
	private String varPrefix;
	
	private int scrollBarWidth=20; 
	private AbsolutePanel root;
	private AbsolutePanel headerContainer;
	private AbsolutePanel gridContainer;
	public SelectionManager selectionManager;
	public InputManager inputManager;
	private Grid header;
	public Grid mainGrid;
	private boolean hasFocus = false;
	private boolean blockFocusChange = false;
	private ScrollPanel scroll;
	private DynGridData dynGridData;
	private GridDataManager gridDataManager;
	
	public DynamicGrid(String elementId){
		this.elementId = elementId;
		this.varPrefix = getVarPrefix(this.elementId);
		dynGridData = new DynGridData(varPrefix);
		gridDataManager = new GridDataManager(elementId,dynGridData.getColumnIds());
		scrollBarWidth = getScrollBarWidth();
		initComponents();
		initWidget(root);
		requestUpdateGeometry();
	}
	
	private void initComponents(){
		int colCount = dynGridData.getColCount();
		
		root = new AbsolutePanel();
		root.getElement().setId(generateId("spreadsheet-Pane"));
		root.setStylePrimaryName("spreadsheet-Pane");
		root.addStyleDependentName("unfocused");
		
		inputManager = new InputManager();
		root.add(inputManager,0,0);
		
		headerContainer = new AbsolutePanel();
		headerContainer.addStyleName("spreadsheet-Header-container");

		header = new Grid();
		header.addStyleName("spreadsheet-Table-head");
		header.resize(1, colCount);
		updateGrid(header, "spreadsheet-Table-cell-head");
		
		for (int i = 0; i<colCount; i++) {
			header.setHTML(0, i, dynGridData.getColName(i));
		}
		
		headerContainer.add(header,0,0);
		gridContainer = new AbsolutePanel();
		
		selectionManager = new SelectionManager();
		gridContainer.add(selectionManager,0,0);
		
		
		mainGrid = new Grid(){
			@Override
			public void onBrowserEvent(Event event) {
				switch (DOM.eventGetType(event)) {
				case Event.ONMOUSEDOWN:
					blockFocusChange = true;
					break;
				case Event.ONMOUSEUP:
					blockFocusChange = false;
					DeferredCommand.addCommand(new Command(){
						public void execute() {
							setComponentFocused(hasFocus);
						}
					});
				}
				super.onBrowserEvent(event);
			}
		};
		mainGrid.sinkEvents(Event.ONMOUSEUP | Event.ONMOUSEDOWN);
		mainGrid.addStyleName("spreadsheet-Table-main");
		int rowCount = Math.max(15, Math.max(dynGridData.getSelectedRow(), 
				gridDataManager.getCurrentRowCount()+1));
		
		mainGrid.resize(rowCount, colCount);
		updateGrid(mainGrid, null);
		
		int mainGridRowCount = mainGrid.getRowCount();
		int mainGridColCount = mainGrid.getColumnCount();
		
		for(int i = 0; i < mainGridColCount; i++){
			for (int j = 0; j < mainGridRowCount; j++ ) {
				String text = gridDataManager.getValue(j, i);
				if (text.length() == 0) {
					text = "&nbsp;";
				}
				mainGrid.setHTML(j, i, text);
			}
		}
		
		
		gridContainer.add(mainGrid,0,0);
		scroll = new ScrollPanel(gridContainer);
		scroll.setStylePrimaryName("spreadsheet-Table-scroll");
		scroll.addScrollListener(this);
		scroll.setAlwaysShowScrollBars(true);
		
		root.add(headerContainer,0,0);
		
		root.add(scroll,0,0);
		
		root.setPixelSize(dynGridData.getWidth(), dynGridData.getHeight());
		headerContainer.setPixelSize(dynGridData.getWidth(), dynGridData.getHeight());
		scroll.setPixelSize(dynGridData.getWidth(), dynGridData.getHeight());
		gridContainer.setPixelSize(dynGridData.getWidth()*3, dynGridData.getHeight()*3);
		
		 
		
		
		selectionManager.setDynamicGrid(this);
		inputManager.setDynamicGrid(this);
		
		DeferredCommand.addCommand(new Command(){
			public void execute() {
				updateGeometryFirstTime();
				selectionManager.setSelection(dynGridData.getSelectedRow(), dynGridData.getSelectedCol());
			}
		});
	}
	
	private void updateGrid(Grid grid,String additionalStyleName) {
		grid.setBorderWidth(0);
		grid.setCellSpacing(0);
		grid.addStyleName("spreadsheet-Table");
	}
	
	public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
		headerContainer.setWidgetPosition(header, -scrollLeft, 0);
		//selectionManager.updateSelectionGeometry();
		//inputManager.updateEditorGeometry();
		dynGridData.setScrollX(scrollLeft);
		dynGridData.setScrollY(scrollTop);
	}
	
	public void requestUpdateGeometry() {
		DeferredCommand.addCommand(new Command(){
			public void execute() {
				updateGeometry();
			}
		});
	}
	
	private void updateGeometryFirstTime() {
		int colCount = dynGridData.getColCount();
		for (int i = 0; i<colCount; i++) {
			String width = "" + dynGridData.getColWidth(i) + "px";
			header.getColumnFormatter().setWidth(i, width);
			mainGrid.getColumnFormatter().setWidth(i, width);
		}
		int headerHeight = header.getOffsetHeight();
		headerContainer.setPixelSize(dynGridData.getWidth() - scrollBarWidth,
				headerHeight);
		
		root.setWidgetPosition(scroll, 0, headerHeight);
		scroll.setPixelSize(dynGridData.getWidth(),
				dynGridData.getHeight() - headerHeight);
		gridContainer.setPixelSize(mainGrid.getOffsetWidth(), mainGrid.getOffsetHeight());
		
		scroll.setHorizontalScrollPosition(dynGridData.getScrollX());
		scroll.setScrollPosition(dynGridData.getScrollY());
		onScroll(null, dynGridData.getScrollX(), dynGridData.getScrollY());
	}
	
	private void updateGeometry() {
		gridContainer.setPixelSize(mainGrid.getOffsetWidth(), mainGrid.getOffsetHeight());
		header.setWidth(""+mainGrid.getOffsetWidth());
		for (int i = 0; i<header.getColumnCount(); i++) {
			int width = mainGrid.getCellFormatter().getElement(0, i).getOffsetWidth();
			header.getColumnFormatter().setWidth(i,""+ width);
			dynGridData.setColWidth(i, width);
		}
		selectionManager.updateSelectionGeometry();
		inputManager.updateEditorGeometry();
	}
	
	public void setComponentFocused(boolean focused){
		hasFocus = focused;
		if (!blockFocusChange) {
			if (focused) {
				root.removeStyleDependentName("unfocused");
				root.addStyleDependentName("focused");
			} else {
				root.removeStyleDependentName("focused");
				root.addStyleDependentName("unfocused");
			}
		}
	}
	
	public void scrollToCell(int row, int col){
		Element cellElem = mainGrid.getCellFormatter().getElement(row, col);
		DOM.scrollIntoView(cellElem);
	}
	    
    private String getVarPrefix(String elementName) {
    	int len = elementName.length();
    	StringBuffer buffer = new StringBuffer(len + 1);
    	int i = 0;
    	char ch = elementName.charAt(i);
    	if (Character.isLetter(ch)) {
    		buffer.append(ch);
    		ch = elementName.charAt(++i);
    	} else {
    		buffer.append('a');
    	}
    	
    	for(; i < len; i++) {
    		ch = elementName.charAt(i);
    		if (Character.isLetter(ch) || Character.isDigit(ch)) {
    			buffer.append(ch);
    		} else {
    			buffer.append('_');
    		}
    	}
    	return buffer.toString();
    }
    
    public String generateId(String id){
    	return elementId + ':'+id;
    }
    
    public void saveSelection(int selectedRow, int selectedCol) {
    	dynGridData.setSelectedRow(selectedRow);
    	dynGridData.setSelectedCol(selectedCol);
    }
    
    public void setCellText(String text, int row, int col) {
		if (text.length() > 0) {
			text = text.replace("&", "&amp;");
			text = text.replace("<", "&lt;");
			text = text.replace(">", "&gt;");
			gridDataManager.setValue(row, col, text);
		} else {
			text="&nbsp;";
			if (row < gridDataManager.getCurrentRowCount()) {
				gridDataManager.setValue(row, col, "");
			}
		}
		mainGrid.setHTML(row, col, text);
		if (row == mainGrid.getRowCount() - 1) {
			mainGrid.resizeRows(mainGrid.getRowCount()+1);
		}
    }
	
    
    /* This code shamelessly borrowed from
     * http://www.alexandre-gomes.com/?p=115
     * 
     */
	private native int getScrollBarWidth() /*-{
		var inner = document.createElement('p');
		inner.style.width = "100%";
		inner.style.height = "200px";

		var outer = document.createElement('div');
		outer.style.position = "absolute";
		outer.style.top = "-1000px";
		outer.style.left = "-1000px";
		outer.style.visibility = "hidden";
		outer.style.width = "200px";
		outer.style.height = "150px";
		outer.style.overflow = "hidden";
		outer.appendChild (inner);

		document.body.appendChild (outer);
		var w1 = inner.offsetWidth;
		outer.style.overflow = 'scroll';
		var w2 = inner.offsetWidth;
		if (w1 == w2) w2 = outer.clientWidth;
		
		document.body.removeChild (outer);
		
		return (w1 - w2);

	}-*/;
	
}
